Skip to content

feat(api): add bulk finding update endpoint#15240

Open
npeham wants to merge 2 commits into
DefectDojo:devfrom
pcode-at:feat/13900-findings-bulk-update-api
Open

feat(api): add bulk finding update endpoint#15240
npeham wants to merge 2 commits into
DefectDojo:devfrom
pcode-at:feat/13900-findings-bulk-update-api

Conversation

@npeham

@npeham npeham commented Jul 14, 2026

Copy link
Copy Markdown

Implements the bulk finding-update capability requested in DefectDojo issue #13900 (bulk create/update/delete API for Findings). This PR delivers the update slice, scoped to a narrow, allowlisted set of threat-intelligence fields.

Description

Adds a new endpoint PATCH /api/v2/findings/bulk/ that updates many findings in a single atomic request, closing the performance and atomicity gap that today forces one API call per finding when enriching EPSS/KEV metadata programmatically.

Design (as discussed in the issue):

  • Request body: {"findings": [{"id": 123, "epss_score": 0.42, ...}, ...]}, at most 200 findings per request.
  • Allowlist: only epss_score, epss_percentile, known_exploited, ransomware_used, kev_date may be set. Any other field is rejected — the endpoint can never be used to mass-edit severity, status or ownership.
  • Validation (400): unknown fields, unknown finding ids, duplicate ids, invalid values (e.g. EPSS out of [0, 1]), empty list, and over-limit batches all return 400 with a clear message.
  • Authorization (403): edit permission is checked per finding, reusing the existing UserHasFindingPermission object permission. If the user cannot edit any referenced finding, the entire batch is rejected with 403 and rolled back — the whole operation runs in one transaction.atomic() block.
  • JIRA: findings are never pushed to JIRA from this endpoint (push_to_jira=False).
  • Audit / history: updates are plain row writes, so the pghistory audit trail is recorded exactly as for a normal PATCH. The dedupe / JIRA / product-grading post-processing — irrelevant to these fields — is skipped for performance.

Implementation:

  • dojo/finding/api/serializer.py: FindingBulkUpdateSerializer + FindingBulkUpdateFieldsSerializer, plus the BULK_UPDATE_ALLOWED_FIELDS / BULK_UPDATE_MAX_FINDINGS constants.
  • dojo/finding/api/views.py: a bulk_update action on FindingViewSet (detail=False, url_path="bulk") with a fully documented @extend_schema.

Test results

Added unittests/test_finding_bulk_update_api.py (13 tests) covering the happy path, response shape, per-field validation, the per-request limit, atomic rollback on a permission failure, unauthenticated access, and that a bulk update produces a pghistory audit event just like a normal PATCH.

All 13 new tests pass, and the existing unittests.test_rest_framework.FindingsTest (27 tests) and unittests.test_apiv2_methods_and_endpoints suites still pass. The generated OpenAPI schema includes the new endpoint and validates cleanly (no drf-spectacular warnings). Ruff-compliant.

Documentation

The endpoint is self-documenting via its @extend_schema request/response and docstring, so it appears in the auto-generated OpenAPI/Swagger API docs. No separate docs-folder page is required for a new API action.

Checklist

  • Features/Changes submitted against the default branch.
  • Code is Ruff compliant (ruff.toml).
  • Code is Python 3.13 compliant.
  • No model changes — no migration needed (fields already exist on Finding).
  • Unit/API tests added.

Extra information

Suggested labels: enhancement, performance, feature.

Add PATCH /api/v2/findings/bulk/ to update an allowlisted set of fields
(epss_score, epss_percentile, known_exploited, ransomware_used, kev_date)
on many findings in a single atomic request. This closes the performance
and atomicity gap for programmatic EPSS/KEV enrichment described in
issue DefectDojo#13900, where updating many findings previously required one API
call per finding.

Behaviour:
- Body: {"findings": [{"id": 123, "epss_score": 0.42, ...}, ...]}, max 200
  findings per request.
- Unknown fields, unknown/duplicate ids, invalid values and over-limit
  batches are rejected with a 400.
- Edit permission is checked per finding; if the user cannot edit any one
  of them the whole batch is rejected with a 403 and rolled back (the work
  runs in a single transaction).
- Findings are never pushed to JIRA from this endpoint.
- Updates are plain row writes, so the pghistory audit trail is recorded
  exactly as for a normal PATCH; the irrelevant dedupe/JIRA/grading
  post-processing is skipped for performance.

Add unit/API tests covering the happy path, validation, the per-request
limit, atomic rollback on a permission failure, and audit history.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015aePDGyHBoxvfpz2iHGmtH
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants